package gulimall.controller;

import com.fasterxml.jackson.core.type.TypeReference;
import gulimall.constant.AuthServerConstant;
import gulimall.exception.BizCodeEnum;
import gulimall.feign.MemberFeignService;
import gulimall.feign.ThirdPartyFeignService;
import gulimall.pojo.bo.UserLoginBO;
import gulimall.pojo.bo.UserRegBO;
import gulimall.utils.HttpUtils;
import gulimall.utils.R;
import gulimall.vo.MemberRespVO;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author HLF
 * @date 2022/7/31 15:14
 */
@Controller
public class LoginRegController {

    @Resource
    private ThirdPartyFeignService thirdPartyFeignService;

    @Resource
    private MemberFeignService memberFeignService;

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Resource(name = "redisTemplate")
    private ValueOperations<String, String> valueOps;

    /**
     * 给指定手机号发送短信验证码
     * 需要解决的问题：
     * TODO 1.接口防刷：
     * 2.验证码再次校验：存到redis中，给一个过期时间
     */
    @GetMapping("/sms/sendCode")
    @ResponseBody
    public R sendCode(@RequestParam(value = "phone") String phone) {
        //先在redis中查看该手机号是否已经在60s之内发送过验证码，如果验证码还在60s期限内就不能发验证码，防止短信轰炸
        String lastCode = valueOps.get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone);
        if (StringUtils.isNotBlank(lastCode)) {
            String lasMills = lastCode.substring(lastCode.lastIndexOf("_") + 1);
            long nowMills = System.currentTimeMillis();
            if (nowMills - Long.parseLong(lasMills) < 60000) {
                return R.error(BizCodeEnum.CODE_TOMUCH_EXCEPTION.getCode(), BizCodeEnum.CODE_TOMUCH_EXCEPTION.getMessage());
            }
        }

        //生成一个随机四位数的验证码，并保存到redis中
        int code = ThreadLocalRandom.current().nextInt(1000, 10000);
        //code和当前系统时间进行拼接，再下一次发送验证码之前验证时间是否超过60s,如果没有超过，就不能发送验证码，实现接口防刷
        String newCode = code + "_" + System.currentTimeMillis();

        //把加了系统时间的验证码保存到redis中并设置过期时间为10分钟
        valueOps.set(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone, newCode, 10, TimeUnit.MINUTES);//过期时间设置为10分钟

        //远程调用发送短信的接口(传递的参数是手机号和真实的验证码<不含系统时间>)
        R r = thirdPartyFeignService.sendCode(phone, String.valueOf(code));
        return r;
    }

    /**
     * 注册，需要从redis中取出短信验证码并验证是否正确
     *
     * @param userRegBO     会员用户注册信息BO
     * @param bindingResult 参数校验错误返回结果
     */
    @PostMapping("/regist")
    public String regist(@Valid UserRegBO userRegBO, BindingResult bindingResult, RedirectAttributes redirectAttributes) {
        Map<String, String> errors;

        //如果出现参数校验错误则重新转发到登录页面
        if (bindingResult.hasErrors()) {

            errors = bindingResult.getFieldErrors().stream()
                    .collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));

            redirectAttributes.addFlashAttribute("errors", errors);//thymeleaf页面直接使用${errors取值}

            return "forward:http://auth.firstgulimall.com/reg.html";
        }

        //对验证码进行验证
        if (StringUtils.isNotBlank(userRegBO.getCode())) {
            String key = AuthServerConstant.SMS_CODE_CACHE_PREFIX + userRegBO.getPhone();
            String redisCodeString = valueOps.get(key);

            String redisCode = "0000";
            if (StringUtils.isNotBlank(redisCodeString)) {
                redisCode = redisCodeString.substring(0, redisCodeString.indexOf("_"));
            }

            if (userRegBO.getCode().equals(redisCode)) {//用户输入的验证码和redis中的验证码相同
                Long expire = redisTemplate.getExpire(key);
                if (expire != null && expire > 0) {
                    //删除redis中的验证码(该验证码使用后使其过期)
                    redisTemplate.delete(key);

                    //远程调用会员服务进行注册
                    R r = memberFeignService.regist(userRegBO);
                    if (r.getCode() == 0) {
                        //注册成功后重定向回登录页
                        return "redirect:http://auth.firstgulimall.com/login.html";
                    }
                } else {
                    redirectAttributes.addFlashAttribute("msg", "验证码已经失效！请重新发送");
                    return "redirect:/http://auth.firstgulimall.com/reg.html";
                }
            } else {
                redirectAttributes.addFlashAttribute("msg", "验证码错误");
                return "redirect:http://auth.firstgulimall.com/reg.html";
            }
        }
        redirectAttributes.addFlashAttribute("msg", "验证码不能为空");
        return "redirect:http://auth.firstgulimall.com/reg.html";
    }

    /**
     * 登录实现
     */
    @PostMapping("/login")
    public String login(@Valid UserLoginBO userLoginBO,
                        BindingResult bindingResult,
                        RedirectAttributes redirectAttributes,
                        HttpSession session) {

        Map<String, String> errors;

        //如果出现参数校验错误则重新转发到登录页面
        if (bindingResult.hasErrors()) {
            errors = bindingResult.getFieldErrors().stream()
                    .collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));

            redirectAttributes.addFlashAttribute("errors", errors);//thymeleaf页面直接使用${errors取值}

            return "redirect:http://auth.firstgulimall.com";
        }

        R r = memberFeignService.login(userLoginBO);
        if (r.getCode() != 0) {
            redirectAttributes.addFlashAttribute("msg", "用户名或密码错误");
            return "redirect:http://auth.firstgulimall.com";
        }

        MemberRespVO memberRespVO = r.get("member", new TypeReference<MemberRespVO>() {
        });
        session.setAttribute(AuthServerConstant.LOGIN_USER, memberRespVO);

        return "redirect:http://firstgulimall.com";
    }


}
